package com.ccp.dev.workflow.event;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ccp.dev.core.basic.util.ContextUtil;
import com.ccp.dev.core.util.AppUtil;
import com.ccp.dev.core.util.BeanUtils;
import com.ccp.dev.core.util.StringUtil;
import com.ccp.dev.form.consts.FormConstants;
import com.ccp.dev.system.model.SysUser;
import com.ccp.dev.system.service.SysUserService;
import com.ccp.dev.workflow.bpmconst.BpmConst;
import com.ccp.dev.workflow.dao.ActDefModelDao;
import com.ccp.dev.workflow.model.ActDefModel;
import com.ccp.dev.workflow.model.BpmNewFlowTrigger;
import com.ccp.dev.workflow.model.BpmUserCondition;
import com.ccp.dev.workflow.model.ProcessRun;
import com.ccp.dev.workflow.model.bpm.ProcessCmd;
import com.ccp.dev.workflow.service.*;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.*;

/**
 * ClassDescribe:
 * 触发新流程事件。
 * @author :wangcheng
 * Date: 2019-12-25
 */

@Service
public class TriggerNewFlowEventListener implements ApplicationListener<TriggerNewFlowEvent>, Ordered {

    @Resource
    BpmNewFlowTriggerService bpmNewFlowTriggerService;
    @Resource
    ActDefModelDao actDefModelDao;
    @Resource
    ProcessRunService processRunService;
    @Resource
    BpmUserConditionService bpmUserConditionService;
    @Resource
    BpmNodeUserService bpmNodeUserService;
    @Resource
    FormHandlerService formHandlerService;
    @Resource
    SysUserService sysUserService;

    @Override
    public void onApplicationEvent(TriggerNewFlowEvent event) {
        TriggerNewFlowModel model = (TriggerNewFlowModel) event.getSource();
        ProcessCmd processCmd = model.getProcessCmd();

        String nodeId = model.getNodeId();
        String action = model.getAction();
        String flowKey = processCmd.getProcessRun().getFlowKey();

        if (StringUtil.isEmpty(action)) {
            return;
        }

        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("thread-call-runner-%d").build();
        ExecutorService executorService = new ThreadPoolExecutor(10,20,200L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),namedThreadFactory);

        try {
            BpmNewFlowTrigger flowTrigger = bpmNewFlowTriggerService.getByNodeAction(nodeId, flowKey, action);
            if (flowTrigger == null) {
                return;
            }
            // 获取映射json
            JSONArray jsonMapping = JSONArray.parseArray(flowTrigger.getJsonMaping());
            if (jsonMapping.size() < 1) {
                return;
            }
            // 上一流程表单字段
            String formDataStr = formHandlerService.getBpmFormDataJson(processCmd.getProcessRun(), processCmd.getBusinessKey(), nodeId);
            if (StringUtil.isEmpty(formDataStr)) {
                return;
            }
            String newFlowKey = flowTrigger.getTriggerFlowKey();
            ActDefModel actDefModel = actDefModelDao.getByProcDefKeyIsMain(newFlowKey);

            // 启动一个新的流程
            ProcessCmd newFlowCmd = new ProcessCmd();
            // 通过映射获取新的表单数据
            setNewFlowFormData(formDataStr, jsonMapping,newFlowCmd);
            newFlowCmd.setModelKey(newFlowKey);
            newFlowCmd.setProcDefId(actDefModel.getProcDefId());
            newFlowCmd.addTransientVar(BpmConst.BPM_DEF, actDefModel);
            // 用户筛选过滤用户
            List<BpmUserCondition> userConditions = bpmUserConditionService.getTriggerNewFlowStartUserConditions(processCmd.getProcDefId(), nodeId);
            List<SysUser> starUsers = bpmNodeUserService.getUserByCondition(processCmd, userConditions, ContextUtil.getCurrentUserId());
            if (BeanUtils.isEmpty(starUsers)) {
                throw new RuntimeException("触发新流程失败，未设置流程发起人！");
            }
            triggerNewFlow(newFlowCmd, sysUserService.getById(starUsers.get(0).getUserId()));

        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("触发新流程失败！:" + e.getMessage());
        } finally {
            executorService.shutdown();
        }
    }

    /**
     * 触发新流程
     * @param newFlowCmd 流程扩展对象
     * @param user
     */
    private void triggerNewFlow(ProcessCmd newFlowCmd, SysUser user) throws Exception {
        if(newFlowCmd == null || user == null){
            throw new RuntimeException("触发新流程失败！ new flow Cmd or starUser cannot be null");
        }

        ContextUtil.setCurrentUser(user);
        ProcessRun run = processRunService.startProcess(newFlowCmd);
    }

    /**
     * 设置新流程表单数据
     * @param formDataStr 表单数据字符串
     * @param jsonMapping 字符串数据映射
     * @return 表单数据
     */

    /**
     * 设置新流程表单数据
     * @param formDataStr 表单数据字符串
     * @param jsonMapping 字符串数据映射
     * @param cmd
     */
    private void setNewFlowFormData(String formDataStr, JSONArray jsonMapping,ProcessCmd cmd) {
        JSONObject newFlowFormData = JSONObject.parseObject("{main:{},sub:[]}");
        //主表字段
        JSONObject formFlowData = JSONObject.parseObject(formDataStr);

        JSONObject mainTableMapping = jsonMapping.getJSONObject(0);
        JSONObject fieldData = formFlowData.getJSONObject("main");
        //主表影射字段
        JSONArray fields = mainTableMapping.getJSONArray("children");
        JSONObject mainFieldData = getFieldDataByFiledMapping(fieldData,fields,newFlowFormData,formFlowData);
        int dataPrivacyLevel = mainFieldData.getInteger("dataPrivacyLevel")==null?1:mainFieldData.getIntValue("dataPrivacyLevel");
        newFlowFormData.getJSONObject("main").put("fields", mainFieldData);
        newFlowFormData.put("opinion", new JSONArray());
        //设置表单数据
        cmd.setFormData(newFlowFormData.toJSONString());
        //设置表单密级
        cmd.addVariable(FormConstants.DATA_PRIVACY_LEVEL,dataPrivacyLevel);
    }

    /**
     * 获取属性数据
     * @param fieldData 属性数据
     * @param fields 表单属性
     * @param newFlowFormData 新流程表单数据
     * @param formFlowData 主表数据
     * @return 主表属性数据
     */
    private JSONObject getFieldDataByFiledMapping(JSONObject fieldData, JSONArray fields, JSONObject newFlowFormData, JSONObject formFlowData) {
        JSONObject mainFieldData = new JSONObject();
        for (int i = 0; i < fields.size(); i++) {
            JSONObject fromField = fields.getJSONObject(i);
            String type  = fromField.getString("fieldType");
            String fieldName = fromField.getString("fieldName").toLowerCase();

            if("table".equals(type)){
                handleSubTableFormData(newFlowFormData,fromField,formFlowData.getJSONObject("sub"));
            }
            if (!fieldData.containsKey(fieldName)) {
                continue;
            }
            JSONArray targets = fromField.getJSONArray("children");
            if (BeanUtils.isNotEmpty(targets)) {
                for (int j = 0; j < targets.size(); j++) {
                    String targetField = targets.getJSONObject(j).getString("fieldName");
                    if ("number".equals(type)) {
                        if (StringUtil.isNotEmpty(fieldData.getString(fieldName))) {
                            mainFieldData.put(targetField, fieldData.getDoubleValue(fieldName));
                        }
                    } else { //("varchar".equals(type))
                        mainFieldData.put(targetField, fieldData.getString(fieldName));
                    }
                }
            }
        }
        return mainFieldData;
    }

    /**
     * 处理字表数据
     * @param newFlowFormData 新流程表单数据
     * @param subTableMapping 子表数据映射
     * @param subTablesJson 字表数据json
     */
    private void handleSubTableFormData(JSONObject newFlowFormData, JSONObject subTableMapping, JSONObject subTablesJson) {
        String subTableName = subTableMapping.getString("triggerTableName");
        if (StringUtil.isEmpty(subTableName)) {
            return;
        }

        String fromTableName = subTableMapping.getString("fieldName");
        JSONObject subTable = new JSONObject();
        subTable.put("tableName", subTableName);
        JSONArray subTableDate = new JSONArray() ;

        if (!subTablesJson.containsKey(fromTableName)) {
            return;
        }
        JSONArray fromsubTableDataArr = subTablesJson.getJSONObject(fromTableName).getJSONArray("dataList");
        for (int i = 0; i < fromsubTableDataArr.size(); i++) {
            JSONObject fields = getFieldDataByFiledMapping(fromsubTableDataArr.getJSONObject(i),subTableMapping.getJSONArray("children"),null,null);
            subTableDate.add(fields);
        }
        subTable.put("fields", subTableDate);
        newFlowFormData.getJSONArray("sub").add(subTable);
    }

    @Override
    public int getOrder() {
        return 3;
    }
}
